package com.alameda.config.interceptors;

import com.alameda.base.constant.ProjectConstant;
import com.alameda.commonpent.event.publisher.EventPublisher;
import com.alameda.commonpent.redis.api.RedisCacheTemplate;
import com.alameda.utils.JwtUtils;
import com.alameda.utils.MapUtils;
import com.alameda.utils.ThreadLocalUtils;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

import static com.alameda.base.constant.LoginConstant.TOKEN;

/**
 * 登录拦截器
 *
 * @author Lv
 * @date 2024/2/19
 */
@Slf4j
@Order(3)
@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisCacheTemplate redisCacheTemplate;

    /**
     * 在处理请求之前进行拦截，进行令牌验证和业务数据存储
     *
     * @param request  http请求对象
     * @param response http响应对象
     * @param handler  处理器对象
     * @return 是否放行请求
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 令牌验证
        String token = request.getHeader("Authorization");
        // 验证token
        try {
            // 从redis中获取相同的token
            String redisToken = redisCacheTemplate.getCacheObject(token);

            if (redisToken == null) {
                // 处理续期
                dealTokenRenewal(token);
                // 二次校验 token是否还处于失效状态
                redisToken = redisCacheTemplate.getCacheObject(token);
                if (redisToken == null) {
                    throw new RuntimeException();
                }
            }
            Map<String, Object> claimMap = JwtUtils.parseToken(token);
            claimMap.put(TOKEN, redisToken);
            // 把业务数据存储到ThreadLocal中
            ThreadLocalUtils.set(claimMap);
            // 放行
            return true;
        } catch (Exception e) {
            // http响应状态码为401
            response.setStatus(401);
            // 不放行
            return false;
        }
    }

    @Autowired
    private EventPublisher eventPublisher;

    /**
     * 处理续期
     *
     * @param token 令牌
     */
    private void dealTokenRenewal(String token) {
        try {
            synchronized (LoginInterceptor.class) {
                log.info("处理 toekn:{} 登录续期", token);
                Map<String, Object> map = MapUtils.newHashMap();
                map.put(TOKEN, token);
                map.put(ProjectConstant.StrategyConstant.LISTEN_STRATEGY, ProjectConstant.StrategyConstant.RENEWAL_STRATEGY);

                // 发送事件 进行token续期
                eventPublisher.publish(JSONObject.toJSONString(map));
            }
            log.info("续期事件发布完成");
        } catch (Exception e) {
            log.error("续期错误", e);
        }
    }

    /**
     * 请求处理完成后清空ThreadLocal中的数据
     *
     * @param request  HttpServletRequest对象
     * @param response HttpServletResponse对象
     * @param handler  处理器对象
     * @param ex       异常对象
     * @throws Exception 异常
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 清空ThreadLocal中的数据
        ThreadLocalUtils.remove();
    }
}